// -*- mode:c++ -*-

// Copyright (c) 2003-2005 The Regents of The University of Michigan
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Steve Reinhardt

////////////////////////////////////////////////////////////////////
//
// Floating-point instructions
//
//      Note that many FP-type instructions which do not support all the
//      various rounding & trapping modes use the simpler format
//      BasicOperateWithNopCheck.
//

output exec {{
    /// Check "FP enabled" machine status bit.  Called when executing any FP
    /// instruction in full-system mode.
    /// @retval Full-system mode: NoFault if FP is enabled, FenFault
    /// if not.  Non-full-system mode: always returns NoFault.
    inline Fault checkFpEnableFault(CPU_EXEC_CONTEXT *xc)
    {
        Fault fault = NoFault;  // dummy... this ipr access should not fault
        if (FullSystem && !ICSR_FPE(xc->readMiscReg(IPR_ICSR))) {
            fault = std::make_shared<FloatEnableFault>();
        }
        return fault;
    }
    inline Fault checkVectorEnableFault(CPU_EXEC_CONTEXT *xc) {
        return std::make_shared<VectorEnableFault>();
    }
}};

output header {{
    /**
     * Base class for general floating-point instructions.  Includes
     * support for various Alpha rounding and trapping modes.  Only FP
     * instructions that require this support are derived from this
     * class; the rest derive directly from AlphaStaticInst.
     */
    class AlphaFP : public AlphaStaticInst
    {
      public:
        /// Alpha FP rounding modes.
        enum RoundingMode {
            Chopped = 0,        ///< round toward zero
            Minus_Infinity = 1, ///< round toward minus infinity
            Normal = 2,         ///< round to nearest (default)
            Dynamic = 3,        ///< use FPCR setting (in instruction)
            Plus_Infinity = 3   ///< round to plus inifinity (in FPCR)
        };

        /// Alpha FP trapping modes.
        /// For instructions that produce integer results, the
        /// "Underflow Enable" modes really mean "Overflow Enable", and
        /// the assembly modifier is V rather than U.
        enum TrappingMode {
            /// default: nothing enabled
            Imprecise = 0,                 ///< no modifier
            /// underflow/overflow traps enabled, inexact disabled
            Underflow_Imprecise = 1,       ///< /U or /V
            Underflow_Precise = 5,         ///< /SU or /SV
            /// underflow/overflow and inexact traps enabled
            Underflow_Inexact_Precise = 7  ///< /SUI or /SVI
        };

      protected:
        /// Map Alpha rounding mode to C99 constants from <fenv.h>.
        static const int alphaToC99RoundingMode[];

        /// Map enum RoundingMode values to disassembly suffixes.
        static const char *roundingModeSuffix[];
        /// Map enum TrappingMode values to FP disassembly suffixes.
        static const char *fpTrappingModeSuffix[];
        /// Map enum TrappingMode values to integer disassembly suffixes.
        static const char *intTrappingModeSuffix[];

        /// This instruction's rounding mode.
        RoundingMode roundingMode;
        /// This instruction's trapping mode.
        TrappingMode trappingMode;

        /// Have we warned about this instruction's unsupported
        /// rounding mode (if applicable)?
        mutable bool warnedOnRounding;

        /// Have we warned about this instruction's unsupported
        /// trapping mode (if applicable)?
        mutable bool warnedOnTrapping;

        /// Constructor
        AlphaFP(const char *mnem, ExtMachInst _machInst, OpClass __opClass)
            : AlphaStaticInst(mnem, _machInst, __opClass),
              roundingMode((enum RoundingMode)FP_ROUNDMODE),
              trappingMode((enum TrappingMode)FP_TRAPMODE),
              warnedOnRounding(false),
              warnedOnTrapping(false)
        {
        }

        int getC99RoundingMode(uint64_t fpcr_val) const;

        // This differs from the AlphaStaticInst version only in
        // printing suffixes for non-default rounding & trapping modes.
        std::string
        generateDisassembly(Addr pc, const SymbolTable *symtab) const;
    };

}};


output decoder {{
    int
    AlphaFP::getC99RoundingMode(uint64_t fpcr_val) const
    {
        if (roundingMode == Dynamic) {
            return alphaToC99RoundingMode[bits(fpcr_val, 59, 58)];
        }
        else {
            return alphaToC99RoundingMode[roundingMode];
        }
    }

    std::string
    AlphaFP::generateDisassembly(Addr pc, const SymbolTable *symtab) const
    {
        std::string mnem_str(mnemonic);

#ifndef SS_COMPATIBLE_DISASSEMBLY
        std::string suffix("");
        suffix += ((_destRegIdx[0].isFloatReg())
                   ? fpTrappingModeSuffix[trappingMode]
                   : intTrappingModeSuffix[trappingMode]);
        suffix += roundingModeSuffix[roundingMode];

        if (suffix != "") {
            mnem_str = csprintf("%s/%s", mnemonic, suffix);
        }
#endif

        std::stringstream ss;
        ccprintf(ss, "%-10s ", mnem_str.c_str());

        // just print the first two source regs... if there's
        // a third one, it's a read-modify-write dest (Rc),
        // e.g. for CMOVxx
        if (_numSrcRegs > 0) {
            printReg(ss, _srcRegIdx[0]);
        }
        if (_numSrcRegs > 1) {
            ss << ",";
            printReg(ss, _srcRegIdx[1]);
        }

        // just print the first dest... if there's a second one,
        // it's generally implicit
        if (_numDestRegs > 0) {
            if (_numSrcRegs > 0)
                ss << ",";
            printReg(ss, _destRegIdx[0]);
        }

        return ss.str();
    }

    const int AlphaFP::alphaToC99RoundingMode[] = {
        M5_FE_TOWARDZERO,       // Chopped
        M5_FE_DOWNWARD, // Minus_Infinity
        M5_FE_TONEAREST,        // Normal
        M5_FE_UPWARD    // Dynamic in inst, Plus_Infinity in FPCR
    };

    const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" };
    // mark invalid trapping modes, but don't fail on them, because
    // you could decode anything on a misspeculated path
    const char *AlphaFP::fpTrappingModeSuffix[] =
        { "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" };
    const char *AlphaFP::intTrappingModeSuffix[] =
        { "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" };
}};

// FP instruction class execute method template.  Handles non-standard
// rounding modes.
def template FloatingPointExecute {{
    Fault %(class_name)s::execute(CPU_EXEC_CONTEXT *xc,
                                  Trace::InstRecord *traceData) const
    {
        if (trappingMode != Imprecise && !warnedOnTrapping) {
            warn("%s: non-standard trapping mode not supported",
                 generateDisassembly(0, NULL));
            warnedOnTrapping = true;
        }

        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
#if USE_FENV
        if (roundingMode == Normal) {
            %(code)s;
        } else {
            m5_fesetround(getC99RoundingMode(
                           xc->readMiscReg(MISCREG_FPCR)));
            %(code)s;
            m5_fesetround(M5_FE_TONEAREST);
        }
#else
        if (roundingMode != Normal && !warnedOnRounding) {
            warn("%s: non-standard rounding mode not supported",
                 generateDisassembly(0, NULL));
            warnedOnRounding = true;
        }
        %(code)s;
#endif

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};

// FP instruction class execute method template where no dynamic
// rounding mode control is needed.  Like BasicExecute, but includes
// check & warning for non-standard trapping mode.
def template FPFixedRoundingExecute {{
    Fault %(class_name)s::execute(CPU_EXEC_CONTEXT *xc,
                                  Trace::InstRecord *traceData) const
    {
        if (trappingMode != Imprecise && !warnedOnTrapping) {
            warn("%s: non-standard trapping mode not supported",
                 generateDisassembly(0, NULL));
            warnedOnTrapping = true;
        }

        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(code)s;

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};

def template FloatingPointDecode {{
 {
     AlphaStaticInst *i = new %(class_name)s(machInst);
     if (FC == 31) {
         i = makeNop(i);
     }
     return i;
 }
}};

// General format for floating-point operate instructions:
// - Checks trapping and rounding mode flags.  Trapping modes
//   currently unimplemented (will fail).
// - Generates NOP if FC == 31.
def format FloatingPointOperate(code, *opt_args) {{
    iop = InstObjParams(name, Name, 'AlphaFP', code, opt_args)
    decode_block = FloatingPointDecode.subst(iop)
    header_output = BasicDeclare.subst(iop)
    decoder_output = BasicConstructor.subst(iop)
    exec_output = FloatingPointExecute.subst(iop)
}};

// Special format for cvttq where rounding mode is pre-decoded
def format FPFixedRounding(code, class_suffix, *opt_args) {{
    Name += class_suffix
    iop = InstObjParams(name, Name, 'AlphaFP', code, opt_args)
    decode_block = FloatingPointDecode.subst(iop)
    header_output = BasicDeclare.subst(iop)
    decoder_output = BasicConstructor.subst(iop)
    exec_output = FPFixedRoundingExecute.subst(iop)
}};

